Computer programs can experience errors during their execution because of many different reasons, such as human errors in coding the program, unavailability of system resources, etc. Examples of such errors include division of a number by zero, calling of a function with insufficient or wrong parameters, etc. Traditional computer languages handle generation of errors during execution of computer programs by returning error values or error messages. Modem languages such as Java and C++ use exception handling to provide error-handling capacities in computer programs. Since initial creation of the C programming language, considerable research and development of programming languages has produced various exception handling mechanisms. An exception is an abnormal event that disrupts the normal execution of a computer program. Exception handling involves, in essence, a program flow deviation in which alternate processing deals with particular error conditions. When a computer program comes across such an exception, an exception handling routine is invoked. This invoking of an exception handling routine is called “throwing an exception.” To invoke an exception handler, a compiler issues a throw statement which identifies an exception type (e.g., file not found). The throw statement is then used to look-up the exception handler. The compiler then redirects program execution to the location of the exception handler identified by the look-up process. This look-up and redirection process is overhead time. Exception handling routines allow programmers to write code that explicitly states what exceptions may be thrown and can be handled. As such, programs that provide exception handling routines are typically more robust and easier to understand than old-fashioned programs that handle errors by returning error values.
There are two commonly used techniques for implementing exception handling upon throwing of an exception. According to one technique, exception throwing is handled by exception tables associated with each function in a program. When an exception is thrown from a location between a set of locations specified in such an exception table, and the exception matches an exception type specified in the exception table, the execution control is transferred to a location also specified in the exception table. Frequently, the code throwing the exception and the code catching the exception are not in the same function or routine of a computer program. Such exceptions are commonly thrown by called functions and they are caught by the caller functions. When stack unwinding is performed during such exception handling, a compiler often cannot do anything to alter the execution of the exception handling process because the necessary code is not available to the compiler in a compiled state (i.e., the code is outside the current compilation scope).
The second technique is to compile the stack unwinding into the native code so as to avoid any run-time overhead. According to this folding technique, the compiler inserts a jump to an exception handler immediately after an instruction throwing an exception. After the exception handler finishes execution, the system continues execution of the code from the location following the exception handler.
As each of these exception handling techniques consumes a considerable amount of processor time, exception throwing should be used only in exceptional cases. However, in reality many programmers use exception throwing as an easy way to change the flow of program control instead of just to handle error events. As a result some of these exception handling routines become hot spots in the program, i.e, such exception handling routines become frequently executed paths and, hence, consume a considerable amount of processing time. The problem of higher processing time along such frequently executed paths becomes more serious when functions are deeply nested. For example when an exception is thrown repeatedly from a function that is deeply nested in a chain of functions calls, the stack unwinding through the entire chain of function calls consumes a considerable amount of processor time as well as other resources such as communications bus bandwidth, memory where the stack has to be unwound, etc.